package com.nulldev.util.web.nTube.pages;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.data.Variables;
import com.nulldev.util.languages.json.uniapi.UniJsonValue;
import com.nulldev.util.web.HttpServer3.Response;
import com.nulldev.util.web.HttpServer3.legacyPages.v1.RequestType;
import com.nulldev.util.web.HttpServer3.pages.v3.HttpPage3;
import com.nulldev.util.web.YouTubeExtractor.channel.ChannelPlaylistExtractor;
import com.nulldev.util.web.YouTubeExtractor.channel.ChannelPlaylistExtractor.ChannelPlaylists;
import com.nulldev.util.web.YouTubeExtractor.extractors.util.YTIAPIClientType;
import com.nulldev.util.web.YouTubeExtractor.frontpage.YTFrontpageExtractor;
import com.nulldev.util.web.YouTubeExtractor.frontpage.YTFrontpageExtractor.Frontpage;
import com.nulldev.util.web.YouTubeExtractor.frontpage.YTFrontpageExtractor.FrontpageSection;
import com.nulldev.util.web.YouTubeExtractor.frontpage.YTFrontpageExtractor.FrontpageVideoEntry;
import com.nulldev.util.web.YouTubeExtractor.metadata.UpdatedMetadataExtractor;
import com.nulldev.util.web.YouTubeExtractor.metadata.YouTubeMetadata;
import com.nulldev.util.web.YouTubeExtractor.playlist.YTPlaylistExtractor;
import com.nulldev.util.web.YouTubeExtractor.playlist.YTPlaylistExtractor.YTPlaylist;
import com.nulldev.util.web.YouTubeExtractor.related.RelatedContinuations;
import com.nulldev.util.web.YouTubeExtractor.related.RelatedParser;
import com.nulldev.util.web.YouTubeExtractor.related.RelatedParser.RelatedInfo;
import com.nulldev.util.web.YouTubeExtractor.related.VideoInfo;
import com.nulldev.util.web.YouTubeExtractor.shorts.YouTubeReelExtractor;
import com.nulldev.util.web.YouTubeExtractor.shorts.YouTubeReelExtractor.ReelType;
import com.nulldev.util.web.YouTubeExtractor.shorts.YouTubeReelExtractor.YouTubeReel;

public class GetPagePage extends HttpPage3 {

	/* 10/10 class name - IGN */

	public GetPagePage() {
		super("/api/get_page");
		super.setCachingMethod(CachingMethod.NO_CACHE);
		super.setUseJSONCaptchaPage(true);
	}

	private static final String JSON_MIME = "application/json; charset=utf-8";

	@SuppressWarnings("resource")
	@Override
	public Response onRequest(final Request request) {
		if (!request.getType().equals(RequestType.POST)) {
			return new Response(405);
		}
		if (!request.hasInputStream()) {
			return new Response(411);
		}
		final String body = toString(request.getInputStream());
		if (!body.startsWith("{") && !body.endsWith("}")) {
			return new Response(400);
		}
		final UniJsonValue object = UniJsonValue.parse(body);
		if (!object.contains("page")) {
			final UniJsonValue response = UniJsonValue.object(2);
			response.add("error", true);
			response.add("reason", "MISSING_PAGE_ITEM");
			return new Response(200, response, response).setCustomMime(JSON_MIME);
		} else {
			final UniJsonValue response = UniJsonValue.object();
			final String page = object.getString("page", null);
			switch (page) {
				case "frontpage": {
					try {
						final Frontpage fp = YTFrontpageExtractor.get();
						if (fp.wasNudged() && fp.sections().isEmpty()) {
							response.add("error", true);
							response.add("reason", "YOUTUBE_FRONTPAGE_NUDGING");
						} else {
							final UniJsonValue result = UniJsonValue.object();
							final UniJsonValue videos = UniJsonValue.array();
							final UniJsonValue sections = UniJsonValue.array();
							for (final FrontpageVideoEntry fpve : fp.videos()) {
								final UniJsonValue video = UniJsonValue.object(5);
								video.add("videoID", fpve.getVideoID());
								video.add("title", fpve.getTitle());
								video.add("author", fpve.getAuthor());
								video.add("duration", fpve.getDuration());
								video.add("thumbnail", "https://i.ytimg.com/vi/" + fpve.getVideoID() + "/mqdefault.jpg");
								videos.add(video);
							}
							result.add("videos", videos);
							for (final FrontpageSection fpSection : fp.sections()) {
								final UniJsonValue section = UniJsonValue.object();
								section.add("title", fpSection.getSectionTitle());
								final UniJsonValue vids = UniJsonValue.array();
								for (final FrontpageVideoEntry fpve : fpSection.videos()) {
									final UniJsonValue video = UniJsonValue.object(5);
									video.add("videoID", fpve.getVideoID());
									video.add("title", fpve.getTitle());
									video.add("author", fpve.getAuthor());
									video.add("duration", fpve.getDuration());
									video.add("thumbnail", "https://i.ytimg.com/vi/" + fpve.getVideoID() + "/mqdefault.jpg");
									vids.add(video);
								}
								section.add("videos", vids);
								sections.add(section);
							}
							if (fp.hasContinuation())
								result.add("continuation", fp.getContinuation());
							result.add("sections", sections);
							response.add("result", result);
						}
						response.add("for", page);
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					} catch (Exception e) {
						JVM.errorln(e);
						response.add("error", true);
						response.add("reason", "INTERNAL_ERROR");
						response.add("for", page);
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					}
				}
				case "playlist": {
					final String playlistID = object.getString("id", null);
					if (!object.contains("id") || playlistID == null || playlistID.isEmpty()) {
						response.add("error", true);
						response.add("reason", "MISSING_PLAYLIST_ID");
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					}
					try {
						final YTPlaylist playlist = YTPlaylistExtractor.getPlaylist(playlistID);
						final UniJsonValue result = UniJsonValue.object();
						result.add("title", playlist.title);
						result.add("length", playlist.entries.size());
						if (playlist.hasContinuation())
							result.add("continuation", playlist.getContinuation());
						final UniJsonValue entries = UniJsonValue.array(playlist.entries.size());
						playlist.entries.forEach((entry) -> {
							final UniJsonValue obj = UniJsonValue.object(6);
							obj.add("title", entry.title);
							obj.add("videoID", entry.videoID);
							obj.add("author", entry.author);
							obj.add("index", entry.index + 1);
							obj.add("duration", entry.duration);
							obj.add("thumbnail", "https://i.ytimg.com/vi/" + entry.videoID + "/mqdefault.jpg");
							entries.add(obj);
						});
						result.add("entries", entries);
						response.add("result", result);
						response.add("for", page);
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					} catch (Exception e) {
						JVM.errorln(e);
						response.add("error", true);
						response.add("reason", "INTERNAL_ERROR");
						response.add("for", page);
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					}
				}
				case "updated_metadata": {
					final String videoID = object.getString("id", null);
					if (!object.contains("id") || videoID == null || videoID.isEmpty()) {
						response.add("error", true);
						response.add("reason", "MISSING_VIDEO_ID");
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					}
					try {
						final YouTubeMetadata ytm = UpdatedMetadataExtractor.getUpdatedMetadata(videoID);
						if (ytm == null || !ytm.isPartialMetadata()) {
							response.add("error", true);
							response.add("reason", "FAILED_TO_EXTRACT_METADATA");
							return new Response(200, response, response).setCustomMime(JSON_MIME);
						}
						final UniJsonValue result = UniJsonValue.object(2);
						result.add("title", ytm.getTitle());
						result.add("viewCount", ytm.getViewCount());
						response.add("result", result);
						response.add("for", page);
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					} catch (Exception e) {
						JVM.errorln(e);
						response.add("error", true);
						response.add("reason", "INTERNAL_ERROR");
						response.add("for", page);
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					}
				}
				case "shorts": {
					final String shortCont = object.getString("cont", null);
					try {
						final YouTubeReel ytr = YouTubeReelExtractor.extract(shortCont != null ? ReelType.WATCH_SEQUENCE : ReelType.ITEM_WATCH, shortCont);
						Variables.requireNonNull(ytr, "ytr");
						final UniJsonValue result = UniJsonValue.object();
						if (ytr.hasContinuation())
							result.add("continuation", ytr.getContinuation());
						final UniJsonValue arr = UniJsonValue.array();
						ytr.getVideos().forEach((video) -> {
							final UniJsonValue obj = UniJsonValue.object(3);
							obj.add("id", video.getVideoID());
							obj.add("title", video.getVideoTitle());
							obj.add("author", video.getVideoAuthor());
							arr.add(obj);
						});
						result.add("videos", arr);
						response.add("result", result);
						response.add("for", page);
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					} catch (Exception e) {
						JVM.errorln(e);
						response.add("error", true);
						response.add("reason", "INTERNAL_ERROR");
						response.add("for", page);
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					}
				}
				case "channel_playlists": {
					final String channelID = object.getString("channelID", null);
					final String cont = object.getString("cont", null);
					if (!object.contains("channelID") || channelID == null || channelID.isEmpty()) {
						response.add("error", true);
						response.add("reason", "MISSING_CHANNEL_ID");
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					}
					try {
						final ChannelPlaylists cl = ChannelPlaylistExtractor.extract(channelID, cont);
						Variables.requireNonNull(cl, "cl");
						final UniJsonValue result = UniJsonValue.object();
						final UniJsonValue arr = UniJsonValue.array();
						cl.playlists().forEach((playlist) -> {
							final UniJsonValue obj = UniJsonValue.object(3);
							obj.add("title", playlist.playlistTitle);
							obj.add("url", playlist.url);
							obj.add("thumbnail", playlist.thumbnailUrl);
							arr.add(obj);
						});
						result.add("videos", arr);
						if (cl.hasContinuation()) {
							result.add("continuaution", cl.continuation());
						}
						response.add("result", result);
						response.add("for", page);
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					} catch (Exception e) {
						JVM.errorln(e);
						response.add("error", true);
						response.add("reason", "INTERNAL_ERROR");
						response.add("for", page);
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					}
				}
				case "related_workaround": {
					final String continuation = object.getString("continuation", null);
					if (continuation == null || continuation.isEmpty()) {
						response.add("error", true);
						response.add("reason", "MISSING_VIDEO_CONTINUATION");
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					}
					final String videoId = object.getString("videoId", null);
					if (videoId == null || videoId.isEmpty()) {
						response.add("error", true);
						response.add("reason", "MISSING_VIDEO_ID");
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					}
					final String clientType = object.getString("clientType", null);
					if (clientType == null || clientType.isEmpty()) {
						response.add("error", true);
						response.add("reason", "MISSING_CLIENT_TYPE");
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					}
					try {
						final UniJsonValue relatedObj = RelatedContinuations.getRelatedObject(videoId, YTIAPIClientType.getFromID(clientType), continuation);
						final RelatedInfo vinfo = RelatedParser.parseArray(relatedObj);
						final UniJsonValue outputArray = UniJsonValue.array(vinfo.size());
						for (final VideoInfo video : vinfo.related()) {
							final UniJsonValue v = UniJsonValue.object(10);
							v.add("title", video.getName());
							v.add("link", "/watch?v=" + video.getID());
							v.add("imgURL", "https://i.ytimg.com/vi/" + video.getThumbnailID() + "/mqdefault.jpg");
							v.add("channel", video.getUser());
							v.add("id", video.getID());
							v.add("duration", video.getDuration());
							v.add("views", video.getViewCount());
							v.add("isPlaylist", video.isPlaylist());
							v.add("isUpNext", video.isUpnext());
							v.add("type", video.getType().getType());
							if (video.hasAnimatedThumbnail()) {
								v.add("ani_thumb", video.getAnimatedThumbnail());
							}
							outputArray.add(v);
						}
						response.add("related", outputArray);
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					} catch (Exception e) {
						JVM.errorln(e);
						response.add("error", true);
						response.add("reason", "INTERNAL_ERROR");
						response.add("for", page);
						return new Response(200, response, response).setCustomMime(JSON_MIME);
					}
				}
				default: {
					response.add("error", true);
					response.add("reason", "UNKNOWN_PAGE");
					response.add("for", page);
					return new Response(200, response, response).setCustomMime(JSON_MIME);
				}
			}
		}
	}

}
